package com.mapbased.da;

import java.net.*;
import java.util.*;

import org.apache.commons.logging.*;
import org.dom4j.*;
import org.dom4j.io.*;
import org.springframework.beans.factory.*;
import org.springframework.jdbc.core.*;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2004</p>
 *
 * <p>Company: </p>
 *
 * @author chy(Baily)
 * @version 1.0
 */
public class MetaFactory
    implements InitializingBean
{
    static final Log log=LogFactory.getLog(MetaFactory.class);

    private JdbcTemplate jdbcTemplate;
    private String fileName;
    private java.util.Map workMap;
    private List createList=new java.util.ArrayList();
    private List dropList=new java.util.ArrayList();
    ;
    private boolean autoDrop;
    private boolean autoCreate;

    public MetaFactory()
    {
        super();
    }

    public FeatureMeta get(String id)
    {
        FeatureMeta fm=(FeatureMeta)this.workMap.get(id);
        if(fm == null)
        {
            throw new java.lang.RuntimeException(
                new StringBuffer("Cannot find FeatureMeta:")
                .append(id)
                .append(" from:")
                .append(this.fileName).toString());
        }
        return fm;
    }

    protected boolean executeMeta(FeatureMeta meta,Map m)
    {
        try
        {
            meta.execute(m);

            return true;
        }
        catch(Exception ex)
        {
            log.warn("Error execute Sql:" + meta.getSql() + " MSG:" +
                     ex.toString());
            // log.debug(ex,ex);
            return false;
        }

    }

    private void create(Map m)
    {

        for(int i=0;i < this.createList.size();i++)
        {

            FeatureMeta meta=(FeatureMeta)this.createList.get(i);

            this.executeMeta(meta,m);
        }

    }

    private void drop(Map m)
    {

        for(int i=0;i < this.dropList.size();i++)
        {
            FeatureMeta meta=(FeatureMeta)this.dropList.get(i);

            this.executeMeta(meta,m);
        }

    }

    public void parse()
        throws MalformedURLException,DocumentException
    {
        if(this.fileName == null)
        {
            this.fileName="sqlmap.xml";
        }

        URL u=Thread.currentThread().getContextClassLoader().getResource(
            fileName);
        if(u == null)
        {
            u=new java.io.File(this.fileName).toURI().toURL();
        }
        SAXReader reader=new SAXReader();
        //   reader.setIncludeExternalDTDDeclarations(false);
        Document doc=reader.read(u);
        Element root=doc.getRootElement();

        String auto=root.attributeValue("auto");
        if(auto == null)
        {
            auto="";
        }
        auto=auto.toLowerCase();
        this.autoCreate=auto.indexOf("create") >= 0;
        this.autoDrop=auto.indexOf("drop") >= 0;

        List works=root.element("work").elements("item");
        Map m=new java.util.HashMap();
        for(int i=0;i < works.size();i++)
        {
            FeatureMeta fm=this.parseItem((Element)works.get(i));
            m.put(fm.getId(),fm);
        }
        this.workMap=java.util.Collections.unmodifiableMap(m);
///////////////////////////
        works=root.element("create").elements("item");
        //Map m=new java.util.HashMap();
        for(int i=0;i < works.size();i++)
        {
            FeatureMeta fm=this.parseItem((Element)works.get(i));
            this.createList.add(fm);
        }
//////////////////////////////////
        works=root.element("drop").elements("item");

        for(int i=0;i < works.size();i++)
        {
            FeatureMeta fm=this.parseItem((Element)works.get(i));
            this.dropList.add(fm);
        }
        ///////////////////////////resolveDepends////////////////////////////////////

        Object[] workmetas=this.workMap.values().toArray();
        for(int i=0;i < workmetas.length;i++)
        {
            FeatureMeta mt=(FeatureMeta)workmetas[i];
            mt.depends=resolveDepends(mt.dependsStr,workMap);
        }

    }

    static final FeatureMeta[] resolveDepends(String s,Map workmap)
    {
        if(s == null || s.trim().equals(""))
        {
            return null;
        }
        String ss[]=s.split(",");
        List l=new ArrayList(ss.length);
        for(int i=0;i < ss.length;i++)
        {
            String ts=ss[i].trim();
            if(ts.equals(""))
            {
                continue;
            }
            Object o=workmap.get(ts);
            if(o == null)
            {
                throw new java.lang.RuntimeException(
                    "Error parse sqlmap:Cannot find depend [" + s + "]");
            }
            l.add(o);

        }
        if(l.size() == 0)
        {
            return null;
        }
        return(FeatureMeta[])l.toArray(new FeatureMeta[l.size()]);

    }

    private FeatureMeta parseItem(Element ele)
    {
        String s=ele.element("sql").getTextTrim();

        String location=ele.element("sql").attributeValue("location");

        if(location != null && location.length() > 1)
        {
            String encoding=ele.element("sql").attributeValue("encoding");

            try
            {
                java.io.InputStream is=
                    Thread.currentThread().getContextClassLoader().
                    getResourceAsStream(location);
                java.io.ByteArrayOutputStream bos=new java.io.
                    ByteArrayOutputStream();
                while(true)
                {
                    int i=is.read();
                    if(i == -1)
                    {
                        break;
                    }
                    bos.write(i);

                }
                try
                {
                    if(encoding == null || encoding.equals(""))
                    {
                        s=bos.toString("utf-8");
                    }
                    else
                    {
                        s=bos.toString(encoding);
                    }
                }
                catch(java.io.UnsupportedEncodingException ue)
                {
                    ue.printStackTrace();
                    s=bos.toString();
                }
                //brfore using iso8859-1,if having utf-8 char ,transalte like properties file
                // and changed now ,don't need to change in ant file

            }
            catch(Exception ex)
            {
                System.err.println("Cannot load res:" + s);
                ex.printStackTrace();
            }

        }

        //      assert s.length() > 1;
        FeatureMeta meta=new FeatureMeta();
        meta.sql=s;
        meta.setJdbcTemplate(this.jdbcTemplate);
        meta.method=FeatureMeta.Method.parser(ele.attributeValue("method"));
        meta.id=ele.attributeValue("id");
        if(meta.id != null)
        {
            meta.id=meta.id.intern();
        }
        meta.dependsStr=ele.attributeValue("depends");
        ///////////////////////outmap////////////////////////////////////
        Element outmapele=ele.element("outmap");
        if(outmapele != null)
        {
            List l=outmapele.elements("entry");
            for(int i=0;i < l.size();i++)
            {
                Element ent=(Element)l.get(i);

                String key=ent.attributeValue("name").intern();
                assert key != null;
                meta.outMap.put(key,ent.attributeValue("class"));
            }
        }
        Element paramsMap=ele.element("parammap");
        if(paramsMap != null)
        {
            List l=paramsMap.elements("entry");
            for(int i=0;i < l.size();i++)
            {
                Element ent=(Element)l.get(i);

                String key=ent.attributeValue("indexkey").intern();
                assert key != null;

                FeatureMeta.InOutParam p=new FeatureMeta.InOutParam();
                p.name=ent.attributeValue("name");
                String dir=ent.attributeValue("direction");
                if(dir != null)
                {
                    dir=dir.trim();
                    if(dir.equalsIgnoreCase("out"))
                    {
                        p.direction=p.OUT;
                    }
                    else if(dir.equalsIgnoreCase("inout"))
                    {
                        p.direction=p.INOUT;
                    }
                }
                meta.paramMap.put(key,p);

            }

        }

        String sql=meta.getSql();
        int pc=0;
        for(int i=0;i < sql.length();i++)
        {
            if(sql.charAt(i) == '#')
            {
                ++pc;
            }
        }
        Element sqlparams=ele.element("sqlmap");
        if(sqlparams != null && pc > 0)
        {
            Map m=new java.util.HashMap();

            List l=sqlparams.elements("entry");
            for(int i=0;i < l.size();i++)
            {
                Element ent=(Element)l.get(i);

                String key=ent.attributeValue("indexkey");
                assert key != null;
                FeatureMeta.SqlParam param=new FeatureMeta.SqlParam();
                param.name=ent.attributeValue("name").intern();
                param.defalutValue=ent.attributeValue("default");
                m.put(key,param);
            }
            // because add "#[i]" support ,remove the assert
            //   assert l.size() >= pc;
            FeatureMeta.SqlParam[] ps=new FeatureMeta.SqlParam[pc];
            for(int i=0;i < ps.length;i++)
            {
                ps[i]=(FeatureMeta.SqlParam)m.get((i + 1) + "");
                // because add "#[i]" support ,remove the assert
                //  assert(ps[i] != null);
            }
            meta.sqlParams=ps;

        }
        //////////////////////////cache///////////////////////////////
        Element cache=ele.element("cache");
        if(cache != null)
        {
            Cache c=new QueryCache(meta);

            c.setLiveSeconds(
                Integer.parseInt(cache.attributeValue("seconds","10")));
            meta.queryCache=c;

        }

        return meta;

    }

    /**
     * Invoked by a BeanFactory after it has set all bean properties supplied
     * (and satisfied BeanFactoryAware and ApplicationContextAware).
     *
     * @throws Exception in the event of misconfiguration (such as failure
     *   to set an essential property) or if initialization fails.
     * @todo Implement this
     *   org.springframework.beans.factory.InitializingBean method
     */
    public void afterPropertiesSet()
        throws Exception
    {
        this.parse();
        this.init(java.util.Collections.EMPTY_MAP);
    }

    public void init(Map m)
    // throws Exception
    {

        if(this.jdbcTemplate == null)
        {
            throw new java.lang.RuntimeException("jdbcTemplate is null");
        }

        if(this.autoDrop)
        {
            this.drop(m);
        }
        if(this.autoCreate)
        {
            this.create(m);
        }

    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate)
    {
        this.jdbcTemplate=jdbcTemplate;

    }

    public void setFileName(String fileName)
    {
        this.fileName=fileName;
    }

    public void autoTest(Map m)
    {
        System.err.println("Metafactory auto test will drop all tables!");

        //force drop all
        this.drop(m);
        //do create,throws exception
        for(int i=0;i < this.createList.size();i++)
        {

            FeatureMeta meta=(FeatureMeta)this.createList.get(i);
            meta.execute(m);

        }
        ////////////test work item////////////////
        Map errors=new java.util.HashMap();
        for(java.util.Iterator i=this.workMap.entrySet().iterator();i.hasNext();)
        {
            Map.Entry e=(Map.Entry)i.next();
            FeatureMeta meta=(FeatureMeta)e.getValue();

            try
            {
                Object o=meta.test(m);

            }
            catch(Exception ex)
            {
                System.out.println("workmap:" + e.getKey() +
                                   "  VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV");

                errors.put(e.getKey(),ex);
                System.out.println(ex);
                System.out.println(
                    "/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\");

                //  ex.printStackTrace();
            }

        }
        System.err.println(errors.toString());

        this.drop(m);

    }
}
